#include "stdafx.h"
#include "scene.h"
#include "sceneobject.h"
#include "isceneobjectvisitor.h"
#include "collisionhandler.h"
#include "model/potion.h"
#include <math.h>
#include "iostream"
#include "time.h"

Scene::Scene(int width, int height) :
    _width(width),
    _height(height),
    _collisionHandler(new CollisionHandler(this)),
    _potionsNumber(0),
    MAX_POTIONS(15)

{
    srand(time(NULL));
}

Scene::~Scene()
{
    for (auto it = _objects.begin(); it != _objects.end(); ++it)
    {
        delete (*it);
    }
    delete _collisionHandler;
}

float Scene::width() const
{
    return _width;
}

float Scene::height() const
{
    return _height;
}

void Scene::registerObject(SceneObject *object)
{
    if (dynamic_cast<Potion *>(object))
    {
        _potionsNumber++;
    }
    // prevent double registering
    for (auto it = _objects.begin(); it != _objects.end(); ++it)
        if (object == *it)
            return;


    _objects.push_back(object);
}

void Scene::deleteObject(SceneObject *object)
{
    if (dynamic_cast<Potion *>(object))
    {
        _potionsNumber--;
    }
    for (auto it = _objects.begin(); it != _objects.end(); ++it)
        if (object == *it)
        {
            _objects.erase(it);
            delete object;
            return;
        }
}

static float distance(SceneObject *first, SceneObject *second)
{
    vec2f diff = vec2f(first->x(), first->y()) - vec2f(second->x(), second->y());
    return diff.getLength();
}

static float mindistance(SceneObject *first, SceneObject *second)
{
    return first->radius() + second->radius();
}

//! NOTE: probably works wrong when more than 2 objects take collision at one place
void Scene::detectCollisions()
{
    bool repeat = true;
    while (repeat)
    {
        repeat = false;
        for (auto it1 = _objects.begin(); it1 != _objects.end(); ++it1)
        {
            for (auto it2 = it1; (++it2) != _objects.end();)
            {
                SceneObject *first = *it1;
                SceneObject *second = *it2;
                if (distance(first, second) <= mindistance(first, second))
                {
                    auto size = _objects.size();
                    _collisionHandler->reset();
                    first->accept(_collisionHandler);
                    second->accept(_collisionHandler);
                    // some objects destroyed, so we should process scene again
                    if (size != _objects.size())
                    {
                        repeat = true;
                        break;
                    }
                }
            }
            if (repeat)
                break;
        }
    }
}

void Scene::accept(ISceneObjectVisitor *visitor) const
{
    for (auto it = _objects.begin(); it != _objects.end();)
    {
        SceneObject *ptr = *it;
        ++it;
        ptr->accept(visitor);
    }
}
void Scene::addPotion()
{

    //std::cout << "Adding potion" << std::endl;
    if (_potionsNumber < MAX_POTIONS)
    {
        //std::cout << _potionsNumber << std::endl;
        int x = rand()%(int)_width;
        int y = rand()%(int)_height;
        bool isMana = rand()%2;
        registerObject(new Potion(isMana,vec2f(x,y)));
    }
}

unsigned int Scene::getPotionsMax()
{
    return MAX_POTIONS;
}

unsigned int Scene::getPotionsNum()
{
    return _potionsNumber;
}
